<template>
  <div class="goods-sku" v-if="list">
    <dl v-for="(item, index) in list" :key="index">
      <dt>{{ item.name }}</dt>
      <dd v-if="list">
        <template v-for="(it, idx) in item.values">
          <span
            v-if="it.picture == null"
            :key="idx"
            :class="{ selected: it.selected, disabled: it.disabled }"
            @click="selected(index, idx)"
            >{{ it.name }}</span
          >
          <img
            :class="{ selected: it.selected, disabled: it.disabled }"
            v-if="it.picture != null"
            @click="selected(index, idx)"
            :key="idx"
            :src="it.picture"
            alt=""
          />
        </template>
      </dd>
    </dl>
  </div>
</template>
<script>
// 引入密集算法
import { bwPowerSet } from "@/utils/Api/DetailApi";
export default {
  name: "GoodsSku",
  props: ["specs", "skus"],
  data() {
    return {
      list: [],
      // 这个变量是控制watch监听中的操作执行一次
      yes: true,
    };
  },
  methods: {
    // 点击规格 让当前规格有样式
    selected(index, idx) {
      // 取反 取消选中
      if (this.list[index].values[idx].selected) {
        this.list[index].values[idx].selected =
          !this.list[index].values[idx].selected;
        return false;
      }
      // 因为更改数据视图会不更新  所以使用this.$set实现
      //先将每一行都添加false 让当前的变为true
      this.list[index].values.forEach((item, k) => {
        this.$set(this.list[index].values[k], "selected", false);
      });
      //当前的为true
      this.$set(this.list[index].values[idx], "selected", true);
      // 点击的时候查看是否需要禁用按钮
      this.checkMap();
      // 全部点击完成 返回父组件需要的数据
      // 怎么知道点击完成规格的数组的长度是否等于总数据的长度 要把undefined去掉
      let arr = this.setSelectArr().filter((v) => v);

      // 有效规格数据
      let mapObj = this.setMap();
      if (arr.length == this.list.length) {
        // 合并数据
        let obj = {};
        let skuName = arr.join("*");
        // 拿到规格的id
        let skuId = mapObj[skuName][0];
        // 通过id在数据中查找对应的数据
        let skuObj = this.skus.find((item) => {
          return item.id == skuId;
        });
        // 拿到对应数据进行数据组合
        obj.skuId = skuId;
        obj.inventory = skuObj.inventory;
        obj.price = skuObj.price;
        obj.oldPrice = skuObj.oldPrice;
        let str = "";
        // 将规格合并成一条字符串
        skuObj.specs.forEach((it) => {
          str += it.name + ":" + it.valueName + "";
        });
        obj.skuName = str;
        // 将父组件需要的数据传递过去
        this.$emit("setSku", obj);
      }
    },
    // 有些没货是不能点的 所以我们要拿到能点的数组
    setMap() {
      let obj = {};
      // 循环遍历数组
      this.skus.forEach((item) => {
        // 判断每一项是否还有库存
        if (item.inventory > 0) {
          //取出每一项有库存的规格里面的规格名字
          let valueNameArr = item.specs.map((it) => {
            return it.valueName;
          });
          //然后通过幂集函数得到所有子集的集合
          let allArr = bwPowerSet(valueNameArr);
          //如果子集都是数组 我们用一个符号链接一下 目的就是让多个数组元素变成字符串好判断
          allArr.forEach((its) => {
            let newName = its.join("*");
            // 将转化成字符串的变量当一个对象的键 值是当前外层的id
            if (obj[newName]) {
              // 在对象中存就追加到值的数组后边
              obj[newName].push(item.id);
            } else {
              // 不存在就直接赋值
              obj[newName] = [item.id];
            }
          });
        }
      });
      //返回这个obj
      return obj;
    },
    // 检查路径是否需要禁用按钮  点击的时候会被调用
    checkMap() {
      // 拿到之前得到的所有有效的路径
      let obj = this.setMap();
      // 遍历所有数据  然后已经选择的就忽略 没有选择的拿到的当前的undefined的位置进行比对看看是否为有效路径
      this.list.forEach((item, key) => {
        // 需要拿到已经选中元素的数组
        let selectArr = this.setSelectArr();
        item.values.forEach((it) => {
          // 怎么知道it是否存在于选中的数组中 如果存在就结束
          if (selectArr.includes(it.name)) return false;
          // 如果不存在就让后边遍历的数据替换undefined
          selectArr[key] = it.name;
          // 将数组连成和我们之前拿到的有效路径一样的数据
          // 一定要先筛选一下 因为之前的undefined也是一个元素 会多出一个符号
          let path = selectArr.filter((v) => v).join("*");
          // 将路径path拿到之前拿到的有效路径中去判断是否存在  然后决定是否禁用
          if (obj[path]) {
            // 存在就不禁用
            it.disabled = false;
          } else {
            // 不存在就禁用
            it.disabled = true;
          }
        });
      });
    },
    // 已经选中元素的数据 结构[] 元素的个数和specs的长度一样长  怎么知道哪个元素被选中了呢  selected属性为true的
    setSelectArr() {
      let arr = [];
      this.list.forEach((item, key) => {
        // 找出当前选中的元素
        let obj = item.values.find((it) => {
          return it.selected;
        });
        // 给数组添加当前元素的名 如果选中就添加名 没选中就添加undefined
        arr[key] = obj ? obj.name : undefined;
      });
      return arr;
    },
  },
  watch: {
    specs: {
      handler() {
        // 进行赋值
        this.list = this.specs;
        // 因为只要修改一次数据就会执行他  所以他会覆盖之前的操作
        if (this.yes) {
          // 现在需要给总数据进行处理  因为要把每一条属性在字典中查找有否存在 存在添加disabled属性为false 不存在添加属性为true
          let obj = this.setMap();
          // 循环遍历数据
          this.list.forEach((item) => {
            item.values.forEach((it) => {
              //如果属性名称在就添加属性
              if (obj[it.name]) {
                it.disabled = false;
              } else {
                // 不存在也添加属性
                it.disabled = true;
              }
            });
          });
          // 执行完操作修改变量为false
          this.yes = false;
        }
      },
      deep: true,
    },
  },
};
</script>
<style scoped lang="scss">
@mixin sku-state-mixin {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  &.selected {
    border-color: #27bb9a;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  //   background-color: white;
  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        @include sku-state-mixin;
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        @include sku-state-mixin;
      }
    }
  }
}
</style>
